package redis

import (
	"encoding/json"
	"reflect"

	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis/v8"

	"gitee.com/csingo/cContext"
	"gitee.com/csingo/cDB"
	"gitee.com/csingo/cLog"

	"gitee.com/csingo/cEvent/interfaces"
)

type Driver struct {
	event interfaces.Event
	db    *redis.Client

	Client string
	Name   string
}

func (i *Driver) LoadDriver(event interfaces.Event) {
	i.event = event
	if i.db == nil {
		i.db = cDB.Client[*redis.Client](cContext.New(), i.Client)
	}
}

func (i *Driver) Publish(ctx *gin.Context, data any) {
	body, err := json.Marshal(data)
	if err != nil {
		cLog.WithContext(ctx, map[string]any{
			"source": "cEvent.redis.Push",
			"app":    i.event.App,
			"name":   i.event.Name,
			"err":    err.Error(),
		}).Error("事件推送失败")
		return
	}
	if err = i.db.Publish(ctx, i.Name, string(body)).Err(); err != nil {
		cLog.WithContext(ctx, map[string]any{
			"source": "cEvent.redis.Push",
			"app":    i.event.App,
			"name":   i.event.Name,
			"err":    err.Error(),
		}).Error("事件推送失败")
	}
}

func (i *Driver) Subscribe() {
	cLog.WithContext(nil, map[string]any{
		"source": "cEvent.redis.Consume",
		"app":    i.event.App,
		"name":   i.event.Name,
	}, []cLog.Option{
		cLog.IncludePaths{},
	}).Trace("启动事件订阅")

	go func() {
		ctx := cContext.New()
		sub := i.db.Subscribe(ctx, i.Name)
		defer sub.Close()

		if _, err := sub.Receive(ctx); err != nil {
			cLog.WithContext(ctx, map[string]any{
				"source": "cEvent.redis.Consume",
				"app":    i.event.App,
				"name":   i.event.Name,
				"err":    err.Error(),
			}).Error("事件订阅失败")
			return
		}

		ch := sub.Channel()
		for data := range ch {
			body := reflect.New(i.event.Type.Elem()).Interface()
			if err := json.Unmarshal([]byte(data.Payload), body); err != nil {
				cLog.WithContext(ctx, map[string]any{
					"source": "cEvent.redis.Consume",
					"app":    i.event.App,
					"name":   i.event.Name,
					"err":    err.Error(),
				}).Error("事件处理失败")
				continue
			}

			if err := i.event.Handler(ctx, body); err != nil {
				cLog.WithContext(ctx, map[string]any{
					"source": "cEvent.redis.Consume",
					"app":    i.event.App,
					"name":   i.event.Name,
					"err":    err.Error(),
				}).Error("事件处理失败")
			}
		}
	}()
}
